package com.china.produce.postProcess;

import com.china.produce.annotation.DCValue;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName
 * @Author niujie
 * @Version
 * @Description
 * @CreateTime 2025/2/16
 */
@Configuration
public class DCValueBeanFactory implements BeanPostProcessor {

    private static final String BASE_CONFIG_PATH = "base_config_path_";

    private final RedissonClient redissonClient;

    private final Map<String, Object> dcObjectMap = new HashMap<>();

    public DCValueBeanFactory(RedissonClient redissonClient) {

        this.redissonClient = redissonClient;
    }

    @Bean("dcTopic")
    public RTopic dccRedisTopicListener(RedissonClient redissonClient) {

        RTopic topic = redissonClient.getTopic("group_buy_market_dc");

        topic.addListener(String.class, (charSequence, s) -> {

            String[] split = s.split(",");
            String attribute = split[0];
            String key = BASE_CONFIG_PATH.concat(attribute);
            RBucket<String> bucket = redissonClient.getBucket(key);
            boolean exists = bucket.isExists();
            if (!exists) return;
            String value = split[1];
            bucket.set(value);
            Object targetObject = dcObjectMap.get(key);
            Class<?> objBeanClass = targetObject.getClass();
            // 检查 objBean 是否是代理对象
            if (AopUtils.isAopProxy(targetObject)) {
                // 获取代理对象的目标对象
                objBeanClass = AopUtils.getTargetClass(targetObject);
            }
            try {
                Field declaredField = objBeanClass.getDeclaredField(attribute);
                declaredField.setAccessible(true);
                declaredField.set(targetObject, value);
                declaredField.setAccessible(false);
            }catch (Exception exception) {
                throw new RuntimeException(exception.getMessage());
            }
        });

        return topic;
    }


    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 注意；增加 AOP 代理后，获得类的方式要通过 AopProxyUtils.getTargetClass(bean); 不能直接 bean.class 因为代理后类的结构发生变化，这样不能获得到自己的自定义注解了。
        Class<?> targetBeanClass = bean.getClass();
        Object targetBeanObject = bean;
        if (AopUtils.isAopProxy(bean)) {
            targetBeanClass = AopUtils.getTargetClass(bean);
            targetBeanObject = AopProxyUtils.getSingletonTarget(bean);
        }
        Field[] fields = targetBeanClass.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(DCValue.class)) {
                continue;
            }
            DCValue dcValue = field.getAnnotation(DCValue.class);

            String value = dcValue.value();
            if (StringUtils.isBlank(value)) {
                throw new RuntimeException(field.getName() + " not empty ");
            }

            String[] split = value.split(",");
            String key = BASE_CONFIG_PATH.concat(split[0]);
            String defaultValue = split.length >= 2 ? split[1] : null;
            if (defaultValue == null) {
                throw new RuntimeException("defaultValue not null");
            }

            RBucket<Object> bucket = redissonClient.getBucket(key);
            boolean exists = bucket.isExists();
            String setValue = defaultValue;
            if (!exists) {
                bucket.set(defaultValue);
            }else {
                setValue = (String) bucket.get();
            }

            try {
                field.setAccessible(true);
                field.set(targetBeanObject, setValue);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            field.setAccessible(false);

            dcObjectMap.put(key, targetBeanObject);
        }

        return bean;
    }
}
